// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现字符串到浮点的转换函数。

package big

import (
	"fmt"
	"io"
	"strings"
)

var floatZero Float

// SetString将z设置为s的值，并返回z和一个表示
// success的布尔值。s必须是一个浮点数，其格式与解析接受的
// 格式相同，基参数为0。整个字符串（不仅仅是前缀）必须
// 才能成功。如果操作失败，z的值为未定义
// 但返回值为零。
func (z *Float) SetString(s string) (*Float, bool) {
	if f, _, err := z.Parse(s, 0); err == nil {
		return f, true
	}
	return nil, false
}

// 扫描与解析类似，但从io读取代表有效
// 浮点数的最长前缀。字节扫描而不是字符串。它将
// 用作Parse的实现。它不识别±Inf，也不希望最后出现
// EOF。
func (z *Float) scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
	prec := z.prec
	if prec == 0 {
		prec = 64
	}

	// 发生错误时的合理值。
	z.form = zero

	// 符号
	z.neg, err = scanSign(r)
	if err != nil {
		return
	}

	// 尾数
	var fcount int // 小数位数计数；如果<=0 
	z.mant, b, fcount, err = z.mant.scan(r, base, true)
	if err != nil {
		return
	}

	// 指数
	var exp int64
	var ebase int
	exp, ebase, err = scanExponent(r, true, base == 0)
	if err != nil {
		return
	}

	// 特例0 
	if len(z.mant) == 0 {
		z.prec = prec
		z.acc = Exact
		z.form = zero
		f = z
		return
	}
	// len（z.mant）>0 

	// 尾数可能有一个基点（fcount<=0），并且那里的
	// 可能是一个非零指数exp。基点等于
	// 除以b**（-fcount）。指数意味着乘以
	// ebase**exp。最后，尾数规范化（左移）需要
	// 2**（-shiftcount）的正确乘法。乘法
	// 是可交换的，所以我们可以以任何顺序应用它们，只要不损失精度。我们只有2和10的幂，而
	// 我们将10的幂分解为
	// 2和5的相同幂的乘积。这减少了基数为10的指数所需的乘法因子
	// 的大小。

	// 规范化尾数并确定初始指数贡献
	exp2 := int64(len(z.mant))*_W - fnorm(z.mant)
	exp5 := int64(0)

	// 确定小数点的二进制或十进制指数贡献
	if fcount < 0 {
		// 尾数有一个小数点ddd。dddd；
		// /-fcount是“.”右边的位数。相应调整相关指数。
		d := int64(fcount)
		switch b {
		case 10:
			exp5 = d
			fallthrough // 10**e==5**e*2**e 
		case 2:
			exp2 += d
		case 8:
			exp2 += d * 3 // 每个
		case 16:
			exp2 += d * 4 // 八进制数字是3位，每个
			exp2 += d * 4 // 十六进制数字是4位，每个
		default:
			panic("unexpected mantissa base")
		}
		// fcount consumed-不再需要
	}

	// 考虑实际指数
	switch ebase {
	case 10:
		exp5 += exp
		fallthrough // 参见上面的故障排除
	case 2:
		exp2 += exp
	default:
		panic("unexpected exponent base")
	}
	// exp consumed-不再需要

	// 应用2**exp2 
	if MinExp <= exp2 && exp2 <= MaxExp {
		z.prec = prec
		z.form = finite
		z.exp = int32(exp2)
		f = z
	} else {
		err = fmt.Errorf("exponent overflow")
		return
	}

	if exp5 == 0 {
		// 无十进制指数贡献
		z.round(0)
		return
	}
	// exp5！=0 

	// 应用5**exp5 
	p := new(Float).SetPrec(z.Prec() + 64) // 为p使用更多位--TODO（gri）正确的数字是多少？
	if exp5 < 0 {
		z.Quo(z, p.pow5(uint64(-exp5)))
	} else {
		z.Mul(z, p.pow5(uint64(exp5)))
	}

	return
}

// 这些5的幂适合于一个uint64。对于p，q:=uint64（0），uint64（1）；p<q；p、 q=q，q*5{
// fmt.Println（q）
// }
// 
var pow5tab = [...]uint64{
	1,
	5,
	25,
	125,
	625,
	3125,
	15625,
	78125,
	390625,
	1953125,
	9765625,
	48828125,
	244140625,
	1220703125,
	6103515625,
	30517578125,
	152587890625,
	762939453125,
	3814697265625,
	19073486328125,
	95367431640625,
	476837158203125,
	2384185791015625,
	11920928955078125,
	59604644775390625,
	298023223876953125,
	1490116119384765625,
	7450580596923828125,
}

// pow5将z设置为5**n并返回z。
// n不能为负。
func (z *Float) pow5(n uint64) *Float {
	const m = uint64(len(pow5tab) - 1)
	if n <= m {
		return z.SetUint64(pow5tab[n])
	}
	// n>m 

	z.SetUint64(pow5tab[m])
	n -= m

	// f的位数比z多
	// TODO（gri）正确的数字是多少？
	f := new(Float).SetPrec(z.Prec() + 64).SetUint64(5)

	for n > 0 {
		if n&1 != 0 {
			z.Mul(z, f)
		}
		f.Mul(f, f)
		n >>= 1
	}

	return z
}

// 解析必须包含浮点数的文本表示形式——
// 在给定的转换基数中带有尾数的点号（指数
// 始终是十进制数），或表示无穷大值的字符串。
// 
// 对于基数0，在基数
// 前缀和相邻数字之间，以及在连续数字之间，可能会出现下划线字符` ` `；这样的
// 下划线不会改变数字的值，或者返回的
// 数字计数。如果没有其他错误，下划线的错误位置将被报告为
// 错误。如果是基地！=0，下划线为
// 无法识别，因此终止扫描，就像任何其他不是有效小数点或数字的字符
// 一样。
// 
// 它将z设置为相应浮点值的（可能四舍五入）值-
// 点值，并返回z、实际基数b和错误err（如果有）。
// 必须使用整个字符串（而不仅仅是前缀）才能成功。
// 如果z的精度为0，则在四舍五入生效之前将其更改为64。
// 数字的格式必须为：
// 
// 数字=[符号]（float |“inf”|“inf”）。
// sign=“+”|“-”。
// 浮点=（尾数|前缀pmantissa）[指数]。
// prefix=“0”[“b”|“b”|“b”|“o”|“o”|“x”]。
// 尾数=位数”[数字]|数字|“数字。
// pmantissa=[““]位”[位数]|[““]位数|”数字。
// 指数=（“e”|“e”|“p”|“p”）[sign]位。
// digits=位{[“”]位}。
// digit=“0”。。。“9”|“a”。。。“z”|“A”。。。“Z”。
// 
// 基参数必须是0、2、8、10或16。提供无效的基本
// 参数将导致运行时恐慌。
// 
// 对于基数0，数字前缀决定实际基数：
// /`0b'或`0b'的前缀选择基数2，`0o'或`0o'选择基数8，
// /`0x'或`0x'选择基数16。否则，实际基数为10，
// 不接受前缀。不支持八进制前缀“0”（前导
// /“0”仅被视为“0”）。
// 
// “p”或“p”指数表示以2为基数（而不是以10为基数）的指数；
// 例如，“0x1.fffffffffff p1023”（使用基数0）表示
// 最大浮点数64。对于十六进制尾数，指数字符
// 必须是“p”或“p”中的一个，如果存在（不能将“e”或“e”指数指示符
// 与尾数数字区分开来）。
// 
// 返回的*Float f为零，z的值有效，但在报告错误时未定义
// 。
// 
func (z *Float) Parse(s string, base int) (f *Float, b int, err error) {
	// 扫描无法处理±Inf 
	if len(s) == 3 && (s == "Inf" || s == "inf") {
		f = z.SetInf(false)
		return
	}
	if len(s) == 4 && (s[0] == '+' || s[0] == '-') && (s[1:] == "Inf" || s[1:] == "inf") {
		f = z.SetInf(s[0] == '-')
		return
	}

	r := strings.NewReader(s)
	if f, b, err = z.scan(r, base); err != nil {
		return
	}

	// 整个字符串必须已被使用
	if ch, err2 := r.ReadByte(); err2 == nil {
		err = fmt.Errorf("expected end of string, found %q", ch)
	} else if err2 != io.EOF {
		err = err2
	}

	return
}

// ParseFloat类似于f.Parse（s，base），f设置为给定精度
// 和舍入模式。
func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) {
	return new(Float).SetPrec(prec).SetMode(mode).Parse(s, base)
}

var _ fmt.Scanner = (*Float)(nil) // /*浮点数必须执行fmt。扫描仪

// 扫描是fmt的一个支持例行程序。扫描仪；它将z设置为
// 扫描号码的值。它接受其动词受
// fmt支持的格式。扫描浮点值，它们是：
// /'b'（二进制）、'e'、'e'、'f'、'g'和'g'。
// 扫描无法处理±Inf。
func (z *Float) Scan(s fmt.ScanState, ch rune) error {
	s.SkipSpace()
	_, _, err := z.scan(byteReader{s}, 0)
	return err
}
